package com.motu.vertx.module.utility.ClassReload;

import com.motu.vertx.module.utility.toolset.Tool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;


public class Scanner {

	private static Scanner instance = new Scanner();
	public static final Logger logger = LoggerFactory.getLogger(Scanner.class);

	public static Scanner getInstance() {
		return instance;
	}

	/**
	 * 目标jar的名字
	 */
//	private static final String JAR_NAME = "firehero-server-game-1.0-SNAPSHOT-fat.jar";

	private String jarPath = null;
	private String jarValue = null;
	private Map<String, Long> nameMapping = new HashMap<String, Long>();
	private List<String> changedFile = new LinkedList<String>();
	private Scan scanner = null;

	/**
	 * 开始热更线程
	 * @throws Exception
	 */
	public void init(String jarName) {
		try {
			//jarPath:/data/appdata/webdata/FireHero/FireHero/firehero-server-game/remoteboot
			jarPath = System.getProperty("user.dir");
			//jarPath:/data/appdata/webdata/FireHero/FireHero/firehero-server-game
			jarPath = jarPath.substring(0, jarPath.lastIndexOf("/"));
			jarPath += "/target/" + jarName;
			logger.info("reload classs jarPath-------->:"+jarPath);
			File file = new File(jarPath);
			if ( file ==null||!file.isFile()) {
				logger.info("file 路径不存在");
				return;
			}
			// 增加基础扫描数据
			scanner = new Scan();
			scanner.scan();
			// 5分钟扫描一次
//			service.scheduleAtFixedRate(new Task(), 10, 10,
//					TimeUnit.SECONDS);
		} catch (Exception e1) {
			logger.error("reload classs init error!!!");
			logger.error(Tool.getException(e1));
		}
	}

	public void reload(){
		try{
			boolean isChanged = scanner.isChanged();
			logger.info("是否发生改变："+isChanged);
			if (isChanged) {
				scanner.reloadClass();
			}
		} catch (Exception e) {
			logger.error(Tool.getException(e));
		}
	}

	class Task implements Runnable {
		@Override
		public void run() {
			try {
				boolean isChanged = scanner.isChanged();
				logger.info("是否发生改变："+isChanged);
				if (isChanged) {
					scanner.reloadClass();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

	}

	class Scan {

		public Scan() {

		}

		public void reloadClass() throws Exception {
			for (String name : changedFile) {
				ClassReloadManager.getInstance().reloadClass(name, jarPath);
			}
			reloadServerVersionClass();// 每次热更强制热更版本号类
			changedFile.clear();
		}

		/**
		 * 热更版本号类
		 */
		public void reloadServerVersionClass() {
			if (changedFile.size() > 0){
				try{
					String className = changedFile.get(0);// com.motu.underground.module.common.utility.ServerVersion
					String[] arr = className.split("\\.");
					String versionClassName = arr[0] + "." + arr[1] + "." + arr[2] + "." + "module.common.utility.ServerVersion";
					logger.info("versionClassName:"+versionClassName);
					// 原本是想每次更新强制更新版本号类，但是经过测试发现如果字节码没有发生变化，热更方法执行没有效果
					ClassReloadManager.getInstance().reloadClass(versionClassName, jarPath);
					Class<?> versionClass = Class.forName(versionClassName);
					Field field = versionClass.getDeclaredField("VERSION");
					logger.info("################## version:" + field.get(versionClass) + " ##################");
				}catch (Exception e){
					logger.error("reloadServerVersionClass error:" + Tool.getException(e));
				}
			}
		}

		public void scan() throws IOException {
			if (jarValue != null) {
				return;
			}
			jarValue = getValue(new File(jarPath));

			JarFile jarFile = new JarFile(jarPath);
			Enumeration<JarEntry> entrys = jarFile.entries();
			String tmpName;
			while (entrys.hasMoreElements()) {
				JarEntry jarEntry = entrys.nextElement();
				if (jarEntry.isDirectory())
					continue;

				tmpName = jarEntry.getName();
				tmpName = tmpName.replaceAll("/", ".");
				if (tmpName.endsWith(".class") && tmpName.indexOf("$") == -1) {// 忽略内部类
					tmpName = tmpName.substring(0, tmpName.lastIndexOf("."));
					nameMapping.put(tmpName, jarEntry.getSize());
				}
			}
			jarFile.close();
		}

		private String getValue(File source) {
			if(!source.exists())
				return jarValue;
						
			String contentLength = String.valueOf((source.length()));
			String lastModified = String.valueOf((source.lastModified()));
			logger.info("contentLength:"+contentLength);
			logger.info("lastModified:"+lastModified);

			return new StringBuilder(contentLength).append(lastModified)
					.toString();
		}

		public boolean isChanged() throws IOException {
			String key = getValue(new File(jarPath));
			logger.info("key："+key);
			logger.info("jarValue:"+jarValue);
			if(key.equals(jarValue))
				return false;
			
			boolean isChanged = false;
			JarFile jarFile = new JarFile(jarPath);
			Enumeration<JarEntry> entrys = jarFile.entries();
			String tmpName;
			while (entrys.hasMoreElements()) {
				JarEntry jarEntry = entrys.nextElement();
				if (jarEntry.isDirectory())
					continue;

				tmpName = jarEntry.getName();
				tmpName = tmpName.replaceAll("/", ".");
				if (tmpName.endsWith(".class") && tmpName.indexOf("$") == -1) {// 忽略内部类
					tmpName = tmpName.substring(0, tmpName.lastIndexOf("."));
					if (nameMapping.containsKey(tmpName)) {
						if (nameMapping.get(tmpName).longValue() != jarEntry
								.getSize()) {
							isChanged = true;
							changedFile.add(tmpName);
							nameMapping.put(tmpName, jarEntry.getSize());
						}
					}

				}
			}
			jarFile.close();

			return isChanged;
		}

	}

}
